//
//  THUIImageWithString.m
//
//  Created by James Montgomerie on 19/09/2008.
//
//  This file is provided "as is" and without any warranty of any kind.
//  All copyrights are disclaimed: this file is released, worldwide, into the
//  public domain.
//

#import "THUIImageWithString.h"


@implementation UIImage (THUIImageWithString)

+ (UIImage *)imageWithString:(NSString *)string // What we want an image of.
                        font:(UIFont *)font     // The font we'd like it in.
                        size:(CGSize)size       // Size of the desired image.
{
    // Create a context to render into.
    UIGraphicsBeginImageContext(size);
    
    // Work out what size of font will give us a rendering of the string
    // that will fit in an image of the desired size. 
    
    // We do this by measuring the string at the given font size and working 
    // out the ratio scale to it by to get the desired size of image.
   
    // Measure the string size.
    CGSize stringSize = [string sizeWithFont:font];
    
    // Work out what it should be scaled by to get the desired size.
    CGFloat xRatio = size.width / stringSize.width;
    CGFloat yRatio = size.height / stringSize.height;
    CGFloat ratio = MIN(xRatio, yRatio);
    
    // Work out the point size that'll give us the desired image size, and
    // create a UIFont that size.
    CGFloat oldFontSize = font.pointSize;
    CGFloat newFontSize = floor(oldFontSize * ratio); 
    ratio = newFontSize / oldFontSize;
    font = [font fontWithSize:newFontSize];

    // What size is the string with this new font?
    stringSize = [string sizeWithFont:font];
    
    // Work out where the origin of the drawn string should be to get it in 
    // the centre of the image.
    CGPoint textOrigin = CGPointMake((size.width - stringSize.width) / 2, 
                                     (size.height - stringSize.height) / 2);
    
    // Draw the string into out image.
    [string drawAtPoint:textOrigin withFont:font];

    // We actually don't have the scaling right, because the rendered
    // string probably doesn't actually fill the entire pixel area of the 
    // box we were given.  We'll use what we just drew to work out the /real/ 
    // size we need to draw at to fill the image.
    
    // First, we work out what area the drawn string /actually/ covered.
    
    // Get a raw bitmap of what we've drawn.
    CGImageRef maskImage = [UIGraphicsGetImageFromCurrentImageContext() 
                                CGImage];
    CFDataRef imageData = CGDataProviderCopyData(
                              CGImageGetDataProvider(maskImage));
    uint32_t *bitmap = (uint32_t *)CFDataGetBytePtr(imageData);
    size_t rowBytes = CGImageGetBytesPerRow(maskImage);
    
    // Now, go through the pixels one-by-one working out the area in which the
    // image is not still blank.
    size_t minx = size.width, maxx = 0, miny = size.height, maxy = 0;
    uint32_t *rowBase = bitmap;
    for(size_t y = 0; y < size.width; ++y, rowBase += rowBytes) {
        uint32_t *component = rowBase;
        for(size_t x = 0; x < size.width; ++x, component += 4) {    
            if(*component != 0) {
                if(x < minx) {
                    minx = x;
                } else if(x > maxx) {
                    maxx = x;
                }
                if(y < miny) {
                    miny = y;
                } else if(y > maxy) {
                    maxy = y;
                }
            }
        }
    }
    CFRelease(imageData); // We're done with this data now.
    
    // Put the area we just found into a CGRect.
    CGRect boundingBox =
        CGRectMake(minx, miny, maxx - minx + 1, maxy - miny + 1);
    
    // We're going to have to move string we're drawing as well as scale it,
    // so we work out how the origin we used to draw the string relates to the 
    // 'real' origin of the filled area.
    CGPoint goodBoundingBoxOrigin = 
        CGPointMake((size.width - boundingBox.size.width) / 2,
                    (size.height - boundingBox.size.height) / 2);
    CGFloat textOriginXDiff = goodBoundingBoxOrigin.x - boundingBox.origin.x;
    CGFloat textOriginYDiff = goodBoundingBoxOrigin.y - boundingBox.origin.y;
    
    // Work out how much we'll need to scale by to fill the entire image.
    xRatio = size.width / boundingBox.size.width;
    yRatio = size.height / boundingBox.size.height;
    ratio = MIN(xRatio, yRatio);
    
    // Now, work out the font size we really need based on our scaling ratio.
    // newFontSize is still holding the size we used to draw with.
    oldFontSize = newFontSize;
    newFontSize = floor(oldFontSize * ratio); 
    ratio = newFontSize / oldFontSize;
    font = [font fontWithSize:newFontSize];
    
    // Work out where to place the string.
    // We offset the origin by the difference between the string-drawing origin 
    // and the 'real' image origin we measured above, scaled up to the new size.
    stringSize = [string sizeWithFont:font];
    textOrigin = CGPointMake((size.width - stringSize.width) / 2, 
                             (size.height - stringSize.height) / 2);
    textOrigin.x += textOriginXDiff * ratio;
    textOrigin.y += textOriginYDiff * ratio;
    
    // Clear the context to remove our old, too-small, rendering.
    CGContextClearRect(UIGraphicsGetCurrentContext(), 
                       CGRectMake(0, 0, size.width, size.height));
    
    // Draw the string again, in the right place, at the right size this time!
    [string drawAtPoint:textOrigin withFont:font];
    
    // We're done!  Grab the image and return it! 
    // (Don't forget to end the image context first though!)
    UIImage *retImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
	
	NSLog(@"imageWithString is finished");
    return retImage;
}

@end
